package com.micro.plugins.jwt.helper;

import com.micro.common.MicroConstant;
import com.micro.common.MicroResponseCode;
import com.micro.core.exception.BizException;
import com.micro.plugins.jwt.base.JWTInfo;
import com.micro.plugins.jwt.base.JWTInfoInterface;
import io.jsonwebtoken.*;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * @desc jwt工具类
 * @author LevelCoder
 * @version V0.0.1
 * @email LevelCoder@126.com
 * @date 2019/6/5
 */
public class JWTHelper {

    /** 引入日志  */
    private static final Logger logger = LoggerFactory.getLogger(JWTHelper.class);

    /** 引入rsa加密工具 */
    private static final RsaKeyHelper rsaKeyHelper = new RsaKeyHelper();

    /**
     * <h2>密钥加密token</h2>
     * @param jwtInfo    jwt 帐号信息
     * @param priKeyPath 私钥地址
     * @param expire     过期时间
     * @return
     * @throws Exception
     */
    public static String generateToken(JWTInfoInterface jwtInfo, byte priKeyPath[], int expire) throws BizException {
        try {
            //设置紧凑型jwt信息
            String compactJws =
                    //返回的字符串便是我们的jwt串了
                    Jwts.builder()
                            //设置主题
                            .setSubject(jwtInfo.getCustomerName())
                            //设置自定义JWT声明参数值 用户编号
                            .claim(MicroConstant.Jwt.JWT_KEY_CUSTOMER_COED, jwtInfo.getCustomerCode())
                            //设置自定义JWT声明参数值 服务名称
                            .claim(MicroConstant.Jwt.JWT_KEY_SERVER_NAME, jwtInfo.getServerName())
                            //设置自定义JWT声明参数值 客户类型
                            .claim(MicroConstant.Jwt.JWT_KEY_CUSTOMER_TYPE,jwtInfo.getCustomerType())
                            //设置自定义JWT声明参数值 用户名称
                            .claim(MicroConstant.Jwt.JWT_KEY_CUSTOMER_NAME,jwtInfo.getCustomerName())
                            //设置自定义JWT声明参数值 请求服务版本/api版本
                            .claim(MicroConstant.Jwt.JWT_KEY_SERVER_VERSION,jwtInfo.getServerVersion())
                            //设置自定义JWT声明参数值 请求渠道类型
                            .claim(MicroConstant.Jwt.JWT_KEY_CHANNEL_TYPE,jwtInfo.getChannelType())
                            //设置生效时间
                            .setExpiration(DateTime.now().plusSeconds(expire).toDate())
                            //设置算法（必须）
                            .signWith(SignatureAlgorithm.RS256, rsaKeyHelper.getPrivateKeyByte(priKeyPath))
                            //这个是全部设置完成后拼成jwt串的方法
                            .compact();
            return compactJws;
        } catch (Exception e) {
            logger.error("message:", e);
            throw new BizException(MicroResponseCode.JWT_GEN_TOKEN_FAIL.getResponseCode(), MicroResponseCode.JWT_GEN_TOKEN_FAIL.getResponseMessage());
        }
    }

    /**
     * <h2>获取token中的用户信息</h2>
     * @param token           需要被校验的token信息
     * @param pubKey          公钥类型为byte[]
     * @return
     * @throws Exception
     */
    public static JWTInfoInterface getInfoFromToken(String token, byte[] pubKey) throws Exception {
        Jws<Claims> claimsJws = parserToken(token, pubKey);
        Claims body = claimsJws.getBody();
        return new JWTInfo(
                ObjectHelper.getObjectValue(body.get(MicroConstant.Jwt.JWT_KEY_CUSTOMER_NAME)),
                ObjectHelper.getObjectValue(body.get(MicroConstant.Jwt.JWT_KEY_CUSTOMER_COED)),
                ObjectHelper.getObjectValue(body.get(MicroConstant.Jwt.JWT_KEY_CUSTOMER_TYPE)),
                ObjectHelper.getObjectValue(body.get(MicroConstant.Jwt.JWT_KEY_SERVER_NAME)),
                ObjectHelper.getObjectValue(body.get(MicroConstant.Jwt.JWT_KEY_SERVER_VERSION)),
                ObjectHelper.getObjectValue(body.get(MicroConstant.Jwt.JWT_KEY_CHANNEL_TYPE))
        );
    }


    /**
     * 公钥解析token
     *
     * @param token
     * @return
     * @throws Exception
     */
    public static Jws<Claims> parserToken(String token, byte[] pubKey) throws Exception {
        try{
            return Jwts.parser().setSigningKey(rsaKeyHelper.getPublicKey(pubKey)).parseClaimsJws(token);
        }catch (ExpiredJwtException ex){
            logger.error("ExpiredJwtException >> User token expired >> e:{}",ex);
            throw new BizException(MicroResponseCode.JWT_TOKEN_EXPIRED.getResponseCode(), MicroResponseCode.JWT_TOKEN_EXPIRED.getResponseMessage());
        }catch (SignatureException ex){
            logger.error("SignatureException >> User token signature error >> e:{}",ex);
            throw new BizException(MicroResponseCode.JWT_SIGNATURE_ERROR.getResponseCode(),MicroResponseCode.JWT_SIGNATURE_ERROR.getResponseMessage());
        }catch (IllegalArgumentException ex){
            logger.error("IllegalArgumentException >> User token is null or empty >> e:{}",ex);
            throw new BizException(MicroResponseCode.JWT_ILLEGAL_ARGUMENT.getResponseCode(),MicroResponseCode.JWT_ILLEGAL_ARGUMENT.getResponseMessage());
        }catch (Exception e) {
            logger.error("message:", e);
            throw new BizException(MicroResponseCode.JWT_PARSER_TOKEN_FAIL.getResponseCode(),MicroResponseCode.JWT_PARSER_TOKEN_FAIL.getResponseMessage());
        }
    }

    /**
     * 校验token有效性
     * @param refreshToken
     * @return
     */
    public static boolean validataToken(String refreshToken,byte[] pubKey) {
        boolean result = true;
        try {
            Jwts.parser().setSigningKey(rsaKeyHelper.getPublicKey(pubKey)).parseClaimsJws(refreshToken);
        }catch (Exception e){
            result  = false;
        }
        return result;
    }
}
